﻿using System.Drawing;
using System.Net;
using System.Net.Sockets;
using System.Reflection;

namespace DAQ.ModbusModule
{
    public class ModbusTcp
    {
        private bool IsMonitored = false; //保证主采集线程只启动一次
        private TcpClient _tcpClient;

        //功能码映射关系
        public readonly Dictionary<string, byte> ReadFuncCodes = new Dictionary<string, byte>();

        //连接状态
        public bool IsConnected => _tcpClient != null && _tcpClient.Connected;

        private DeviceLink _link;
        private List<RegisterPoint> _points;

        public event Action<RegisterPoint, object> ValueUpdated;

        byte[] _readbytes = new byte[]
        {
            0x00,
            0x01,
            0x00,
            0x00,
            0x00,
            0x06,
            0x01,
            0x01,
            0x00,
            0x01,
            0x00,
            0x10
        };

        public ModbusTcp(DeviceLink link)
        {
            if (link == null)
            {
                throw new Exception("未初始化参数");
            }

            _link = link;
            _points = link.Points;
            _tcpClient = new TcpClient();
            Init();
        }

        void Init()
        {
            //初始化关系
            ReadFuncCodes.Add("Coil", 1);
            ReadFuncCodes.Add("DiscreteInput", 2);
            ReadFuncCodes.Add("HoldingRegister", 3);
            ReadFuncCodes.Add("InputRegister", 4);

            _readbytes[6] = Convert.ToByte(_link.SlaveId); //修改从站地址
        }

        /// <summary>
        /// 连接
        /// </summary>
        private void Connect()
        {
            try
            {
                _tcpClient.Connect(IPAddress.Parse(_link.Ip), _link.Port);
            }
            catch (Exception ex) { }
        }

        public void DoMitor()
        {
            if (IsMonitored)
            {
                return;
            }
            IsMonitored = true;

            ThreadPool.QueueUserWorkItem(o =>
            {
                while (true)
                {
                    try
                    {
                        if (!IsConnected)
                        {
                            Connect();
                        }
                        else
                        {
                            var client = _tcpClient.Client;

                            foreach (var point in _points)
                            {
                                //修改功能码
                                _readbytes[7] = ReadFuncCodes[point.RegisterType];

                                //修改起始地址
                                var addressBytes = BitConverter.GetBytes(point.Address);
                                _readbytes[8] = addressBytes[1];
                                _readbytes[9] = addressBytes[0];

                                //修改查询数量
                                var LengthBytes = BitConverter.GetBytes(point.Length);
                                _readbytes[10] = LengthBytes[1];
                                _readbytes[11] = LengthBytes[0];

                                //发送报文
                                client.Send(_readbytes);

                                int len;
                                if (_readbytes[7] <= 2)
                                {
                                    len = point.Length / 8 + 1;
                                }
                                else
                                {
                                    len = point.Length * 2;
                                }
                                var errLen = 9;
                                //正常长度
                                var normalLen = 9 + len;
                                //接收报文
                                byte[] recvBytes = new byte[normalLen];

                                //报文总长
                                int recevTotalLength = 0;

                                while (recevTotalLength < normalLen && recevTotalLength != errLen)
                                {
                                    try
                                    {
                                        int needLength = normalLen - recevTotalLength;
                                        var recvLen = client.Receive(
                                            recvBytes,
                                            recevTotalLength,
                                            needLength,
                                            SocketFlags.None
                                        );
                                        if (recvLen == 0)
                                        {
                                            throw new Exception("未接收到响应数据");
                                        }
                                        recevTotalLength += recvLen;
                                    }
                                    catch (Exception ex)
                                    {
                                        throw new Exception("接收响应数据异常!" + ex.Message);
                                    }
                                }
                                if (recevTotalLength == errLen)
                                {
                                    throw new Exception("查询异常");
                                }
                                byte[] dataBytes = new byte[len];
                                Array.Copy(recvBytes, 9, dataBytes, 0, len);
                                DealData(point, dataBytes);
                            }
                        }

                        Thread.Sleep((int)(_link.AcqTimeSpan * 1000));
                    }
                    catch (Exception ex)
                    {
                        Console.WriteLine(ex.ToString());
                    }
                }
            });
        }

        /// <summary>
        /// 数据处理方法
        /// </summary>
        /// <param name="point"></param>
        /// <param name="bytes"></param>
        void DealData(RegisterPoint point, byte[] bytes)
        {
            //TODO:处理返回数据
            var funcCode = ReadFuncCodes[point.RegisterType];
            object value;

            if (funcCode <= 2)
            {
                //单点查询，线圈只查询一个，byte中不等于0，就是true
                value = bytes[0] != 0;
            }
            else
            {
                if (point.Type == typeof(byte))
                {
                    value = bytes[1];
                }
                else
                {
                    //字节序处理
                    byte[] newbytes = new byte[point.Length * 2];
                    //优化
                    for (int i = 0; i < point.Length; i++)
                    {
                        newbytes[2 * i] = bytes[2 * i + 1];
                        newbytes[2 * i + 1] = bytes[2 * i];
                    }

                    var method = typeof(BitConverter).GetMethod(
                        "To" + point.Type.Name,
                        BindingFlags.Public | BindingFlags.Static,
                        new[] { typeof(byte[]), typeof(int) }
                    );
                    value = method.Invoke(null, new object[] { newbytes, 0 });
                }
            }
            if (!value.Equals(point.Value))
            {
                point.Value = value;
                ValueUpdated?.Invoke(point, value);
            }

            //if (point.Value == null || value.ToString() != point.Value.ToString())
            //{
            //    point.Value = value;
            //    ValueUpdated?.Invoke(point, value);
            //}
        }
    }
}
